package com.lengyu.iottransfer.gateway.common;

import com.lengyu.iottransfer.gateway.tcp.daxiang.DaxiangGuanpianTransfer;
import io.netty.buffer.Unpooled;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.util.Locale;
import java.util.zip.CRC32;

public class ByteUtils {
    private static Logger logger = LoggerFactory.getLogger(ByteUtils.class);
    private static final int POLYNOMIAL_CRC16_XMODEM = 0x1021;

    public static byte[] hexStrToByteArray(String str) {
        if (StringUtils.isBlank(str))
            return null;
        String strs = str.replace(" ","");
        if (StringUtils.isBlank(strs))
            return null;
        return strToByteArray(strs);
    }

    public static int getBitValueInByte(int leftIndex,byte b) {
        return ((b >> leftIndex) & 0x1);
    }

    public static byte[] strToByteArray(String str) {
        if (StringUtils.isBlank(str) || str.length() % 2 != 0)
            return null;
        str = str.toLowerCase();
        for (int i = 0; i < str.length(); i++) {
            char a = str.charAt(i);
            if (a <= '9' && a >= '0')
                continue;
            else if (a <= 'f' && a >= 'a')
                continue;
            else
                return null;
        }
        byte[] data = new byte[str.length() / 2];
        for (int i = 0; i < str.length(); i += 2) {
            char a1 = str.charAt(i);
            char a2 = str.charAt(i + 1);
            // log.info((int) a1 + " " + (int) a2);
            byte b1 = charToByte(a1);
            byte b2 = charToByte(a2);
            byte b = (byte) ((b1 << 4) | b2);
            data[i / 2] = b;
        }
        return data;
    }

    private static byte charToByte(char a) {
        if (a <= '9' && a >= '0')
            return (byte) ((a - '0') & 0xff);
        else if (a <= 'f' && a >= 'a')
            return (byte) (((a - 'a') + 10) & 0xff);
        else
            return (byte) (a & 0xff);
    }

    public static int getHigh4(byte data) {// 获取高四位
        int height;
        height = ((data & 0xf0) >> 4);
        return height;
    }

    public static int getLow4(byte data) {// 获取低四位
        int low;
        low = (data & 0x0f);
        return low;
    }

    // java无论平台变化，都是高字节开头
    public static byte[] shortToByteArray(short a) {
        return new byte[] { (byte) ((a >> 8) & 0xFF), (byte) (a & 0xFF) };
    }

    /*
     * /高字节在前，低字节在后
     */
    public static int byteArrayToInt(byte[] data, int offset, int len2) {
        int len = len2 <= 4 ? len2 : 4;
        int[] b = new int[] { 0, 0, 0, 0 };

        for (int i = offset; i < offset + len; i++) {
            byte a = data[i];
            int a1 = a & 0xff;
            b[i - offset] = a1;
        }
        return (int) (b[3] & 0xFF | ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8));
    }

    /*
     * /高字节在前，低字节在后
     */
    public static float byteArrayToFloat(byte[] data, int offset, int len2) {
        byte[] bytes = new byte[len2];
        System.arraycopy(data,offset,bytes,0,len2);

        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.put(bytes);
        buffer.flip(); // 需要调换顺序读取
        return buffer.getFloat();
    }

    /*
     * /高字节在前，低字节在后
     */
    public static float byteArrayToFloatReverse(byte[] data, int offset, int len2) {
        byte[] bytes = new byte[len2];
        System.arraycopy(data,offset,bytes,0,len2);

        byte[] newBytes = new byte[len2];
        newBytes[0] = bytes[2];
        newBytes[1] = bytes[3];
        newBytes[2] = bytes[0];
        newBytes[3] = bytes[1];

        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.put(newBytes);
        buffer.flip(); // 需要调换顺序读取
        return buffer.getFloat();
    }

    /**
     *
     * @description: 高字节在前，低字节在后
     * @param bytes
     * @return long
     * @author liuyu
     * @date 2019年1月15日 下午4:33:59
     * =================Modify Record=================
     * @Modifier	     @date			       @Content
     *  liuyu           2019年1月15日 下午4:33:59	         新建
     *
     */
    public static long byteArrayToLong(byte[] bytes) {
        byte[] b = new byte[8];
        if (bytes == null || bytes.length > 8) {
            return -1;
        } else if (bytes.length < 8) {
            System.arraycopy(bytes, 0, b, b.length - bytes.length, bytes.length);
        }
        long s0 = b[7] & 0xff;// 最低位
        long s1 = b[6] & 0xff;
        long s2 = b[5] & 0xff;
        long s3 = b[4] & 0xff;
        long s4 = b[3] & 0xff;// 最低位
        long s5 = b[2] & 0xff;
        long s6 = b[1] & 0xff;
        long s7 = b[0] & 0xff;
        // s0不变
        s1 <<= 8;
        s2 <<= 16;
        s3 <<= 24;
        s4 <<= 8 * 4;
        s5 <<= 8 * 5;
        s6 <<= 8 * 6;
        s7 <<= 8 * 7;
        return s0 | s1 | s2 | s3 | s4 | s5 | s6 | s7;

    }

    /**
     * byte[] 转有符号数
     * @param data
     * @param offset
     * @param len
     * @return
     */
    // 高字节在前，低字节在后
    public static short byteArrayToShort(byte[] data, int offset, int len) {
        byte[] bytes = new byte[2];
        for (int i = offset; i < offset + len; i++) {
            bytes[i - offset] = data[i];
        }
        return (short) ((bytes[0] << 8) | (bytes[1] & 0xFF));
    }

    // 高字节在前，低字节在后
    public static short byteArrayToShort(byte[] data, int offset) {
        byte[] bytes = new byte[2];
        for (int i = offset; i < offset + 2; i++) {
            bytes[i - offset] = data[i];
        }
        return (short) ((bytes[0] << 8) | (bytes[1] & 0xFF));
    }


    // 低字节在高位，高字节在低位
    public static short byteArrayToShortReverse(byte[] data, int offset) {
        byte[] bytes = new byte[2];
        for (int i = offset; i < offset + 2; i++) {
            bytes[i - offset] = data[i];
        }
        return (short) ((bytes[1] << 8) | (bytes[0] & 0xFF));
    }

    /**
     *
     * @description: 低字节在高位，高字节在低位
     * @param data
     * @return short
     * @author liuyu
     * @date 2019年1月15日 下午2:26:35
     * =================Modify Record=================
     * @Modifier	     @date			       @Content
     *  liuyu           2019年1月15日 下午2:26:35	         新建
     *
     */
    public static short byteArrayToShortReverse(byte[] data) {
        if (data == null || data.length < 2) {
            return -1;
        }
        return (short) (data[0] & 0xFF | (data[1] & 0xFF) << 8);
    }

    /**
     * 两位byte转无符号数字
     * @param bytes
     * @param offset
     * @return
     */
    public static int byteArrayToUnsignedInt(byte[] bytes, int offset){
        byte a = bytes[offset];
        byte b = bytes[offset+1];
        return Unpooled.wrappedBuffer(new byte[]{a, b}).readUnsignedShort();

    }

    /**
     * 两位byte转无符号数字
     * @param bytes
     * @param offset
     * @return
     */
    public static long byteArrayToUnsignedLong(byte[] bytes, int offset){
        byte a = bytes[offset];
        byte b = bytes[offset+1];
        byte c = bytes[offset+2];
        byte d = bytes[offset+3];
        return Unpooled.wrappedBuffer(new byte[]{a, b,c,d}).readUnsignedInt();

    }

    /**
     *
     * @description: 低字节在高位高字节在地位
     * @param data
     * @return int
     * @author liuyu
     * @date 2019年1月15日 下午2:27:07
     * =================Modify Record=================
     * @Modifier	     @date			       @Content
     *  liuyu           2019年1月15日 下午2:27:07	         新建
     *
     */
    public static int byteArrayToIntReverse(byte[] data) {
        if (data == null || data.length < 4) {
            return -1;
        }
        return (int) (data[0] & 0xFF | ((data[3] & 0xFF) << 24) | ((data[2] & 0xFF) << 16) | ((data[1] & 0xFF) << 8));
    }

    /**
     *
     * @description: 低字节在高位高字节在地位
     * @param data
     * @return int
     * @author liuyu
     * @date 2019年1月15日 下午2:27:07
     * =================Modify Record=================
     * @Modifier	     @date			       @Content
     *  liuyu           2019年1月15日 下午2:27:07	         新建
     *
     */
    public static int byteArrayToIntReverse(byte[] data, int offset, int len2) {
        int len = len2 <= 4 ? len2 : 4;
        int[] b = new int[] { 0, 0, 0, 0 };
        for (int i = offset; i < offset + len; i++) {
            byte a = data[i];
            int a1 = a & 0xff;
            b[i - offset] = a1;
        }
        return (int) (b[0] & 0xFF | ((b[3] & 0xFF) << 24) | ((b[2] & 0xFF) << 16) | ((b[1] & 0xFF) << 8));
    }

//    public static String toHexString(byte[] data) {
//        if (data == null)
//            return "";
//        else
//            return HexUtil.toHexString(data, 0, data.length);
//    }

    public static String byteArrayToHexString(byte[] b){
        return byteArrayToHexString(b,0,b.length,true);
    }


    public static String byteArrayToHexString(byte[] data,int offset,int length,boolean spaceFlag){
        byte[] useData = new byte[length];
        System.arraycopy(data,offset,useData,0,length);
        try{
            String result = "";
            for (int i=0; i < useData.length; i++) {
                result +=
                        Integer.toString( ( useData[i] & 0xff ) + 0x100, 16).substring( 1 );
                if(spaceFlag){
                    if(i<useData.length - 1){
                        result += " ";
                    }
                }
            }
            return result;
        }
        catch (Exception ex){
            return "";
        }
    }


    /**
     *
     * @description: 将0~255范围内的整数，转换为java的byte类型，注意byte类型的取值范围为-128~127
     * @param a 0~255范围内的整数
     * @return byte   java表示的字节
     * @author liubo
     * @date 2018年5月13日 下午4:37:43
     */
    public static byte intToByte(int a) {
        byte b = (byte) (a & 0xff);
        return b;
    }

    /**
     *
     * @description: 16进制单字节累加和校验值
     * @param data
     * @return byte
     * @date 2018年11月6日 上午11:57:01
     * =================Modify Record=================
     * @Modifier	     @date			       @Content
     *
     */
    public static byte makeChecksum(byte[] data) {
        int total = 0;
        for (int i = 0; i < data.length; i++) {
            int value = (int) (data[i] & 0xff);
            total += value;
            total = total & 0xff;
        }
        byte bRes = (byte) (total & 0xff);
        return bRes;
    }

    /**
     * 计算CRC16校验码
     *
     * @param data 需要校验的字符串
     * @return 校验码
     */
    public static String getCRC16(String data,boolean reverse) {
            data = data.replace(" ", "");
//        int len = data.length();
//        if (!(len % 2 == 0)) {
//            return "0000";
//        }
//        int num = len / 2;
//        byte[] para = new byte[num];
//        for (int i = 0; i < num; i++) {
//            int value = Integer.valueOf(data.substring(i * 2, 2 * (i + 1)), 16);
//            para[i] = (byte) value;
//        }

        byte[] para =  strToByteArray(data);


        String crc16Str = getCRC16(para,reverse);
        return crc16Str;
    }


    /**
     * @param bytes 字节数组
     * @return {@link String} 校验码
     */
    public static String getCRC16(byte[] bytes,boolean reverse) {
        //CRC寄存器全为1
        int CRC = 0x0000ffff;
        //多项式校验值
        int POLYNOMIAL = 0x0000a001;
        int i, j;
        for (i = 0; i < bytes.length; i++) {
                CRC ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                        CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                        CRC >>= 1;
                }
            }
        }
        //结果转换为16进制
        String result = Integer.toHexString(CRC).toUpperCase();
        if (result.length() != 4) {
                StringBuffer sb = new StringBuffer("0000");
            result = sb.replace(4 - result.length(), 4, result).toString();
        }
        if(reverse){
            //交换高低位
            return result.substring(2, 4)  + " " + result.substring(0, 2);
        }
        else{
            return result.substring(0, 2)  + " " + result.substring(2, 4);
        }

    }

    public static String getCRC32Hex(byte[] data){

        CRC32 crc32 = new CRC32();
        crc32.update(data);
        long crcValue = crc32.getValue();

        String hexString = Long.toHexString(crcValue);

        return getHexWithSpace(hexString);
    }


    public static String getCRC16_XMODEM_Hex(byte[] data,int offset,int length,boolean reverse){
        byte[] useData = new byte[length];
        System.arraycopy(data,offset,useData,0,length);
        int crc = 0x00;

        int polynomial = 0x1021;
        for (int index = 0; index < useData.length; index++) {
            byte b = useData[index];
            for (int i = 0; i < 8; i++) {
                boolean bit = ((b >> (7 - i) & 1) == 1);
                boolean c15 = ((crc >> 15 & 1) == 1);
                crc <<= 1;
                if (c15 ^ bit)
                    crc ^= polynomial;
            }
        }
        crc &= 0xffff;

        String result = Integer.toHexString(crc).toLowerCase();
        if(reverse){
            //交换高低位
            return result.substring(2, 4)  + " " + result.substring(0, 2);
        }
        else{
            return result.substring(0, 2)  + " " + result.substring(2, 4);
        }
    }

    public static String getCRC32Hex(String hexStr){
        byte[] data = hexStrToByteArray(hexStr);
        return getCRC32Hex(data);

    }

    public static String longToHexStr(long data){
        String hexString = Long.toHexString(data);
        return getHexWithSpace(hexString);
    }

    public static String byteToHex(byte data){
        String hexString = Integer.toString( ( data & 0xff ) + 0x100, 16).substring( 1 );
        return hexString;
    }

    public static int byteToInt(byte data){
        int intData =  data & 0xff;;
        return intData;
    }

    public static String getHexWithSpace(String str){
        StringBuilder sb = new StringBuilder();
        if(str.length() % 2 != 0){
            str = "0" + str;
        }
        for(int i = 0;i < str.length() / 2 ;i++){
            sb.append(str.substring(i*2,(i+1) * 2));
            if(i!=(str.length() / 2) - 1){
                sb.append(" ");
            }
        }
        return sb.toString();
    }
}
